//------------------------------------------------------------
//  Filename: eth_mac_rmii2mii.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-11-24 17:23
//  Description: 
//   
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_rmii2mii ( 
    input  logic           rstn_i,
    
    input  logic           refclk,
    input  logic [1:0]     rxd,
    input  logic           crsdv,
    
    output logic [1:0]     txd,
    output logic           tx_en,
    
    output logic           tx_clk,
    input  logic [3:0]     tx_data,
    input  logic           tx_data_v,

    output logic           rx_clk,
    output logic [3:0]     rx_data,
    output logic           rx_data_v,

    output logic           phy_crs,
    output logic           phy_col
);   
//--------------------------------------------------------
logic       clk_i;
//--------------------------------------------------------
assign      clk_i = refclk;
//--------------------------------------------------------
logic       mac_clkout;
logic       speed_10;
logic [5:0] rx_cnt,rx_cnt_q;
logic       rx_en;

logic [3:0] rx_nibble;
logic       rx_dibit_h;

logic       tx_dibit_h;
logic [2:0] tx_mii_bin;
logic [2:0] tx_mii_gray;
logic [2:0] tx_rmii_gray;
logic [2:0] tx_rmii_ff1;
logic [2:0] tx_rmii_bin;

logic [1:0] rxd_ff1;
logic       crsdv_ff1;
logic       crsdv_ff2;
logic       crsdv_keep;

//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rxd_ff1 <= 1'b0;
    end
    else begin
        rxd_ff1 <= rxd;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        crsdv_ff1 <= 1'b0;
        crsdv_ff2 <= 1'b0;
    end
    else begin
        crsdv_ff1 <= crsdv;
        crsdv_ff2 <= crsdv_ff1;
    end
end
//--------------------------------------------------------
assign crsdv_keep = crsdv_ff2|crsdv_ff1;
//--------------------------------------------------------
// data sync
//--------------------------------------------------------
enum logic[3:0] {SLEEP,IDLE,PREAMB,SFD,DATA,LOSTDV} cs,ns;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        cs <= SLEEP;
    end
    else begin
        cs <= ns;
    end
end
//--------------------------------------------------------
always_comb begin
    ns = cs;
    rx_cnt = rx_cnt_q;
    rx_en  = rx_data_v;
    case(cs)
        SLEEP: begin
            rx_en = 0;
            ns = (crsdv_ff1&(rxd_ff1 == 2'b01)) ? IDLE : cs;
        end
        IDLE: begin
            rx_cnt = 1;
            ns = (rxd_ff1 == 2'b01) ? PREAMB : cs;
        end
        PREAMB: begin
            rx_en  = 1;
            rx_cnt = rx_cnt_q + 1;
            ns = (rxd_ff1 == 2'b11) ? SFD : cs;
        end
        SFD: begin
            ns = DATA;
        end
        DATA: begin
            ns = (~crsdv_ff1) ? LOSTDV : cs;
        end
        LOSTDV: begin
            ns = crsdv_keep ? cs : SLEEP;
        end
    endcase
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rx_cnt_q  <= 'b0;
        rx_data_v <= 1'b0;
    end
    else begin
        rx_cnt_q  <= rx_cnt;
        rx_data_v <= rx_en;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        speed_10 <= 'b0;
    end
    else begin
        speed_10 <= ((cs == DATA)&&(rx_cnt_q > 32)) ? 1'b1 : 1'b0;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rx_nibble <= 4'b0;
    end
    else begin 
        rx_nibble <= {rxd_ff1,rx_nibble[3:2]};
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rx_data <= 4'b0;
    end
    else if(rx_dibit_h) begin 
        rx_data <= rx_nibble;
    end
end
//--------------------------------------------------------
// Clock generate
logic [3:0] div_cnt;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(rstn_i == 1'b0) begin
        div_cnt <= 4'b0;
    end
    else if((cs == IDLE)&&(rxd_ff1 == 2'b01)) begin
        div_cnt <= 4'b0;
    end
    else if(speed_10)begin
        div_cnt <= (div_cnt < 9)?(div_cnt + 1):4'b0;
    end
    else begin
        div_cnt[0] <= ~div_cnt[0];   
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        rx_dibit_h <= 1'b0;
    end
    else if(cs == SLEEP) begin 
        rx_dibit_h <= 1'b0;
    end
    else if(cs == IDLE) begin
        rx_dibit_h <= (rxd_ff1 == 2'b01) ? 1'b1 : 1'b0;    
    end
    else if(speed_10) begin 
        rx_dibit_h <= (div_cnt == 0)?(~rx_dibit_h):rx_dibit_h;
    end
    else begin
        rx_dibit_h <= ~rx_dibit_h;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        mac_clkout <= 1'b0;
    end
    else if(~speed_10)begin
        mac_clkout <= div_cnt[0];
    end
    else if (speed_10&&(div_cnt == 9)) begin
        mac_clkout <= ~mac_clkout;
    end
end
//--------------------------------------------------------
assign rx_clk = mac_clkout;         
//--------------------------------------------------------
//---
//---
//--------------------------------------------------------
assign tx_clk = mac_clkout;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin
    if(~rstn_i) begin
        tx_dibit_h <= 1'b0;
    end
    else if((~speed_10)||(speed_10&&(div_cnt == 9)))begin
        if(tx_data_v) tx_dibit_h <= ~tx_dibit_h;
        else          tx_dibit_h <= 1'b0;
    end
end
//--------------------------------------------------------
always_comb begin
    tx_mii_bin[2]   = tx_data_v;
    tx_mii_bin[1:0] = tx_data[2*tx_dibit_h +: 2];
end
//--------------------------------------------------------
assign tx_mii_gray  = (tx_mii_bin >> 1) ^ tx_mii_bin;
//--------------------------------------------------------
always_ff @(negedge clk_i,negedge rstn_i) begin
    if(!rstn_i) begin
        tx_rmii_ff1  <= 0;
        tx_rmii_gray <= 0;
    end
    else begin
        tx_rmii_ff1  <= tx_mii_gray;
        tx_rmii_gray <= tx_rmii_ff1;
    end
end
//--------------------------------------------------------
function logic[2:0] gray2bin(input logic[2:0] gray_in);
    logic[7:0] i,j;
    logic[2:0] bin_code;
    for(i=0;i<=2;i++)  begin
        bin_code[i]=gray_in[i];
        for(j=i;j<2;j++) bin_code[i]=bin_code[i]^gray_in[j+1];
    end
    gray2bin=bin_code;
endfunction
//--------------------------------------------------------
assign tx_rmii_bin = gray2bin(tx_rmii_gray);
//--------------------------------------------------------
always_ff @(negedge clk_i,negedge rstn_i) begin
    if(!rstn_i) begin
        txd   <= 2'b0;
        tx_en <= 1'b0;
    end
    else begin
        txd   <= tx_rmii_bin[1:0] ;
        tx_en <= tx_rmii_bin[2]   ;        
    end
end

endmodule
